#include "command.hpp"
#include <QDebug>


Command::Command(QObject *parent) :
    QObject(parent)
{
    this->setAdressProgramMemory(-1);
    this->setCommand(Command::NOCOMMAND);
}


Command::~Command()
{
}


void Command::setAdressProgramMemory(int adress)
{
    this->adressProgramMemory = adress;

    QString anchor = QString::number(adress, 16);
    while (anchor.size() < 4)
	anchor.prepend("0");
    this->setAnchor(anchor.toUpper());
}


void Command::setOpCode(int code)
{
    this->opCode = code;
    this->setCommand(extractCommand(opCode));
}


void Command::setArguments(int arg)
{
    this->argumentsThreeBit = (arg & 896) >> 7; // Get the 3 bit argument specifying the bit in a file
    this->argumentsSevenBit = arg & 127; // Get the last 7 bits
    this->argumentsEightBit = arg & 255; // Get an 8 bit argument
    this->argumentsElevenBit = arg & 2047; // Get 11 bits
    this->directoryBit = (arg & 128) >> 7; // Get the directory bit
}


void Command::setAnchor(QString anchor)
{
    this->anchor = anchor;
}


void Command::setLineNumber(int line)
{
    this->lineNumber = line;
}


void Command::setComment(QString comment)
{
    this->comment = comment;
}


void Command::setLabel(QString label)
{
    this->label = label.trimmed();
    this->setAnchor(label.trimmed());
}


void Command::setAssemblerCode(QString asmCode)
{
    this->assembler = asmCode;
}


void Command::setCommandType(commandType opType)
{
    this->type = opType;
}


void Command::setCommand(commandEnum command)
{
    this->command = command;
}


int Command::getAddress() const
{
    return this->adressProgramMemory;
}


int Command::getOpCode() const
{
    return this->opCode;
}


int Command::getArgument(argumentType argument) const
{
    switch (argument)
    {
    case Command::THREEBIT:
	return this->argumentsThreeBit;

    case Command::SEVENBIT:
	return this->argumentsSevenBit;

    case Command::EIGHTBIT:
	return this->argumentsEightBit;

    case Command::ELEVENBIT:
	return this->argumentsElevenBit;

    case Command::DIRECTORYBIT:
	return this->directoryBit;

    default:
	return 0;
    }

    return 0;
}


int Command::getLineNumber() const
{
    return this->lineNumber;
}


QString Command::getLabel() const
{
    return this->label;
}


QString Command::getComment() const
{
    return this->comment;
}


QString Command::getAssembler() const
{
    return this->assembler;
}


QString Command::getAnchor() const
{
    return this->anchor;
}


Command::commandType Command::getCommandType() const
{
    return this->type;
}


Command::commandEnum Command::getCommand() const
{
    return this->command;
}


Command::commandEnum Command::extractCommand(int code) const
{
    int mask = 0b11100000000000;
    if ((code & mask) == Command::CALL)
    {
	return Command::CALL;
    } else if ((code & mask) == Command::GOTO)
    {
	return Command::GOTO;
    }

    
    mask = 0b11110000000000;
    if ((code & mask) == Command::MOVLW)
    {
	return Command::MOVLW;
    } else if ((code & mask) == Command::RETLW)
    {
	return Command::RETLW;
    } else if ((code & mask) == Command::BCF)
    {
	return Command::BCF;
    } else if ((code & mask) == Command::BSF)
    {
	return Command::BSF;
    } else if ((code & mask) == Command::BTFSC)
    {
	return Command::BTFSC;
    } else if ((code & mask) == Command::BTFSS)
    {
	return Command::BTFSS;
    }

    
    mask = 0b11111000000000;
    if ((code & mask) == Command::ADDLW)
    {
	return Command::ADDLW;
    } else if ((code & mask) == Command::SUBLW)
    {
	return Command::SUBLW;
    }
    
    
    mask = 0b11111100000000;
    switch (code & mask) {
    case Command::ADDWF:
	return Command::ADDWF;
	break;
    case Command::ANDWF:
	return Command::ANDWF;
	break;
    case Command::COMF:
	return Command::COMF;
	break;
    case Command::DECF:
	return Command::DECF;
	break;
    case Command::DECFSZ:
	return Command::DECFSZ;
	break;
    case Command::INCF:
	return Command::INCF;
	break;
    case Command::INCFSZ:
	return Command::INCFSZ;
	break;
    case Command::IORWF:
	return Command::IORWF;
	break;
    case Command::MOVF:
	return Command::MOVF;
	break;
    case Command::RLF:
	return Command::RLF;
	break;
    case Command::RRF:
	return Command::RRF;
	break;
    case Command::SUBWF:
	return Command::SUBWF;
	break;
    case Command::SWAPF:
	return Command::SWAPF;
	break;
    case Command::XORWF:
	return Command::XORWF;
	break;
    case Command::ANDLW:
	return Command::ANDLW;
	break;
    case Command::IORLW:
	return Command::IORLW;
	break;
    case Command::XORLW:
	return Command::XORLW;
	break;
    }
    
    
    mask = 0b11111110000000;
    if ((code & mask) == Command::CLRF)
    {
	return Command::CLRF;
    } else if ((code & mask) == Command::CLRW)
    {
	return Command::CLRW;
    } else if ((code & mask) == Command::MOVWF)
    {
	return Command::MOVWF;
    }
    
    
    if (code == Command::CLRWDT)
    {
	return Command::CLRWDT;
    } else if (code == Command::CLRWDT)
    {
	return Command::CLRWDT;
    } else if (code == Command::RETFIE)
    {
	return Command::RETFIE;
    } else if (code == Command::RETURN)
    {
	return Command::RETURN;
    } else if (code == Command::SLEEP)
    {
	return Command::SLEEP;
    } else if (code == Command::NOP)
    {
	return Command::NOP;
    }

    return Command::NOCOMMAND;
}


QString Command::toString() const
{
    switch (this->type)
    {	
    case Command::BLANK:
	return "";

    case Command::COMMENT:
	return this->comment;

    case Command::LABEL:
	return this->label;

    case Command::COMMAND:
    case Command::DEFINITION:
    case Command::ADDITIONAL:
	return this->assembler;

    default:
	return "";
    }

    return "";
}
